package com.lc1993929.dubbo.boot.service;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConstructorArgumentValues;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.boot.autoconfigure.AutoConfigurationPackages;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

import java.util.LinkedHashSet;
import java.util.Set;

/**
 * Created by LiuChang on 2017/9/12/012.
 * dubbo的扫描器的自动配置
 */
@Configuration
public class DubboScanAutoConfiguration implements ImportBeanDefinitionRegistrar, BeanFactoryAware {


    private static final String BEAN_NAME = "dubboServiceBeanPostProcessor";

    private BeanFactory beanFactory;

    /**
     * 实现{@link BeanFactoryAware}接口，这样就可以直接获取bean了
     *
     * @param beanFactory
     * @throws BeansException
     */
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        this.beanFactory = beanFactory;
    }

    /**
     * 实现{@link ImportBeanDefinitionRegistrar}接口，动态的向spring容器中注入{@link BeanDefinition}
     *
     * @param importingClassMetadata
     * @param registry
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        Set<String> packagesToScan = new LinkedHashSet<String>();
        packagesToScan.addAll(AutoConfigurationPackages.get(this.beanFactory));//获取包的根路径
        if (registry.containsBeanDefinition(BEAN_NAME)) {
            //如果spring容器中已经有想要注入的definition，则更新
            updatePostProcessor(registry, packagesToScan);
        } else {
            //如果spring容器中还没有想要注入的definition，则注入
            addPostProcessor(registry, packagesToScan);
        }
    }

    /**
     * 更新{@link DubboServiceBeanPostProcessor}生成时的构造方法的参数
     *
     * @param registry
     * @param packagesToScan
     */
    private void updatePostProcessor(BeanDefinitionRegistry registry,
                                     Set<String> packagesToScan) {
        BeanDefinition definition = registry.getBeanDefinition(BEAN_NAME);
        ConstructorArgumentValues.ValueHolder constructorArguments = definition.getConstructorArgumentValues()
                .getGenericArgumentValue(Set.class);
        @SuppressWarnings("unchecked")
        Set<String> mergedPackages = (Set<String>) constructorArguments.getValue();
        mergedPackages.addAll(packagesToScan);
        constructorArguments.setValue(mergedPackages);
    }

    /**
     * 添加{@link DubboServiceBeanPostProcessor}注册到spring中，并且注入在创建bean时的构造参数
     *
     * @param registry
     * @param packagesToScan
     */
    private void addPostProcessor(BeanDefinitionRegistry registry,
                                  Set<String> packagesToScan) {
        GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
        beanDefinition.setBeanClass(DubboServiceBeanPostProcessor.class);
        beanDefinition.getConstructorArgumentValues()
                .addGenericArgumentValue(packagesToScan);
        beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
        registry.registerBeanDefinition(BEAN_NAME, beanDefinition);
    }
}
